<template><div><h2 id="认证" tabindex="-1"><a class="header-anchor" href="#认证"><span>认证</span></a></h2>
<p>认证是鉴定用户身份的过程。它通常使用一个标识符，例如用户名、手机号或电子邮件地址等，再加上一个加密令牌，比如密码、短信验证码或存取令牌（access token）等，然后用它们来认证用户的身份。认证是登录功能的基础。</p>
<p>Yii提供了一个认证框架，它连接了不同的组件以支持登录。欲使用这个框架，你主要需要做以下工作：</p>
<ul>
<li>设置用户组件 <code v-pre>yii\web\User</code>;</li>
<li>创建一个类实现 <code v-pre>yii\web\IdentityInterface</code> 接口。</li>
</ul>
<h2 id="配置用户组件" tabindex="-1"><a class="header-anchor" href="#配置用户组件"><span>配置用户组件</span></a></h2>
<p>用户组件 <code v-pre>yii\web\User</code> 用来管理用户的认证状态。这需要你指定一个含有实际认证逻辑的认证类 <code v-pre>yii\web\User::identityClass</code>。在以下web应用的配置项中，将用户用户组件 <code v-pre>yii\web\User</code> 的认证类 <code v-pre>yii\web\User::identityClass</code> 配置成模型类，例如：<code v-pre>app\models\User</code>，它的实现将在下一节中讲述。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'components'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'user'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'identityClass'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\models\User'</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="认证接口的实现" tabindex="-1"><a class="header-anchor" href="#认证接口的实现"><span>认证接口的实现</span></a></h2>
<p>认证类 <code v-pre>yii\web\User::identityClass</code> 必须实现包含以下方法的 认证接口 <code v-pre>yii\web\IdentityInterface</code>：</p>
<ul>
<li><code v-pre>yii\web\IdentityInterface::findIdentity()</code>：根据指定的用户ID查找认证模型类的实例，当你需要使用 session 来维持登录状态的时候会用到这个方法。</li>
<li><code v-pre>yii\web\IdentityInterface::findIdentityByAccessToken()</code>：根据指定的存取令牌查找认证模型类的实例，该方法用于通过单个加密令牌认证用户的时候（比如无状态的 RESTful 应用）。</li>
<li><code v-pre>yii\web\IdentityInterface::getId()</code>：获取该认证实例表示的用户的 ID。</li>
<li><code v-pre>yii\web\IdentityInterface::getAuthKey()</code>：获取基于 cookie 自动登录时使用的认证密钥。认证密钥储存在 cookie 里并且将来会与服务端的版本进行比较以确保 cookie 的有效性。</li>
<li><code v-pre>yii\web\IdentityInterface::validateAuthKey()</code>：是基于 cookie 登录密钥的验证的逻辑的实现。</li>
</ul>
<p>用不到的方法可以空着，例如，你的项目只是一个无状态的 RESTful 应用，只需实现 <code v-pre>yii\web\IdentityInterface::findIdentityByAccessToken()</code> 和 <code v-pre>yii\web\IdentityInterface::getId()</code>，其他的方法的函数体留空即可。</p>
<p>我们可以通过一个结合了 <code v-pre>user</code> 数据表的 AR 模型 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md" target="_blank" rel="noopener noreferrer">Active Record</a> 实现的一个认证类 <code v-pre>yii\web\User::identityClass</code>，代码如下：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>IdentityInterface</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">User</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span> <span class="token keyword">implements</span> <span class="token class-name">IdentityInterface</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">tableName</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token string single-quoted-string">'user'</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * 根据给到的ID查询身份。</span>
<span class="line">     *</span>
<span class="line">     * <span class="token keyword">@param</span> <span class="token class-name"><span class="token keyword">string</span><span class="token punctuation">|</span><span class="token keyword">integer</span></span> <span class="token parameter">$id</span> 被查询的ID</span>
<span class="line">     * <span class="token keyword">@return</span> <span class="token class-name">IdentityInterface<span class="token punctuation">|</span><span class="token keyword">null</span></span> 通过ID匹配到的身份对象</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">findIdentity</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token keyword static-context">static</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * 根据 token 查询身份。</span>
<span class="line">     *</span>
<span class="line">     * <span class="token keyword">@param</span> <span class="token class-name"><span class="token keyword">string</span></span> <span class="token parameter">$token</span> 被查询的 token</span>
<span class="line">     * <span class="token keyword">@return</span> <span class="token class-name">IdentityInterface<span class="token punctuation">|</span><span class="token keyword">null</span></span> 通过 token 得到的身份对象</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">findIdentityByAccessToken</span><span class="token punctuation">(</span><span class="token variable">$token</span><span class="token punctuation">,</span> <span class="token variable">$type</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token keyword static-context">static</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'access_token'</span> <span class="token operator">=></span> <span class="token variable">$token</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * <span class="token keyword">@return</span> <span class="token class-name"><span class="token keyword">int</span><span class="token punctuation">|</span><span class="token keyword">string</span></span> 当前用户ID</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">id</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * <span class="token keyword">@return</span> <span class="token class-name"><span class="token keyword">string</span></span> 当前用户的（cookie）认证密钥</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getAuthKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">auth_key</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * <span class="token keyword">@param</span> <span class="token class-name"><span class="token keyword">string</span></span> <span class="token parameter">$authKey</span></span>
<span class="line">     * <span class="token keyword">@return</span> <span class="token class-name"><span class="token keyword">boolean</span></span> if auth key is valid for current user</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">validateAuthKey</span><span class="token punctuation">(</span><span class="token variable">$authKey</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">getAuthKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token variable">$authKey</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如上所述，如果你的应用利用 cookie 登录，你只需要实现 <code v-pre>getAuthKey()</code> 和 <code v-pre>validateAuthKey()</code> 方法。这样的话，你可以使用下面的代码在 <code v-pre>user</code> 表中生成和存储每个用户的认证密钥。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">User</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span> <span class="token keyword">implements</span> <span class="token class-name">IdentityInterface</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token operator">...</span><span class="token operator">...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">beforeSave</span><span class="token punctuation">(</span><span class="token variable">$insert</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">beforeSave</span><span class="token punctuation">(</span><span class="token variable">$insert</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">isNewRecord</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">                <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">auth_key</span> <span class="token operator">=</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">security</span><span class="token operator">-></span><span class="token function">generateRandomString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">            <span class="token punctuation">}</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token constant boolean">true</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token constant boolean">false</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Note: 不要混淆 <code v-pre>user</code> 认证类和用户组件 <code v-pre>yii\web\User</code>。前者是实现认证逻辑的类，通常用关联了持久性存储的用户信息的 AR 模型 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md" target="_blank" rel="noopener noreferrer">Active Record</a> 实现。后者是负责管理用户认证状态的应用组件。</p>
</blockquote>
<h2 id="使用用户组件" tabindex="-1"><a class="header-anchor" href="#使用用户组件"><span>使用用户组件</span></a></h2>
<p>在 <code v-pre>user</code> 应用组件方面，你主要用到 <code v-pre>yii\web\User</code> 。</p>
<p>你可以使用表达式 <code v-pre>Yii::$app-&gt;user-&gt;identity</code> 检测当前用户身份。它返回一个表示当前登录用户的认证类 <code v-pre>yii\web\User::identityClass</code> 的实例，未认证用户（即游客）则返回 null。下面的代码展示了如何从 <code v-pre>yii\web\User</code> 获取其他认证相关信息：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 当前用户的身份实例。未认证用户则为 Null 。</span></span>
<span class="line"><span class="token variable">$identity</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">user</span><span class="token operator">-></span><span class="token property">identity</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 当前用户的ID。 未认证用户则为 Null 。</span></span>
<span class="line"><span class="token variable">$id</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">user</span><span class="token operator">-></span><span class="token property">id</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 判断当前用户是否是游客（未认证的）</span></span>
<span class="line"><span class="token variable">$isGuest</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">user</span><span class="token operator">-></span><span class="token property">isGuest</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>你可以使用下面的代码登录用户：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 使用指定用户名获取用户身份实例。</span></span>
<span class="line"><span class="token comment">// 请注意，如果需要的话您可能要检验密码</span></span>
<span class="line"><span class="token variable">$identity</span> <span class="token operator">=</span> <span class="token class-name static-context">User</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span> <span class="token operator">=></span> <span class="token variable">$username</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 登录用户</span></span>
<span class="line"><span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">user</span><span class="token operator">-></span><span class="token function">login</span><span class="token punctuation">(</span><span class="token variable">$identity</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code v-pre>yii\web\User::login()</code> 方法将当前用户的身份登记到 <code v-pre>yii\web\User</code>。如果 session 设置 <code v-pre>yii\web\User::enableSession</code> 属性值为 true，则使用 session 记录用户身份，用户的认证状态将在整个会话中得以维持。如果开启自动登录 <code v-pre>yii\web\User::enableAutoLogin</code> 属性为 true，则基于 cookie 自动登录（如：记住登录状态），它将使用 cookie 保存用户身份，这样只要 cookie 有效就可以恢复登录状态。</p>
<p>为了使用 cookie 自动登录，你需要在应用配置文件中将 <code v-pre>yii\web\User::enableAutoLogin</code> 属性值设为 true。你还需要在 <code v-pre>yii\web\User::login()</code> 方法中传递有效期（记住登录状态的时长）参数。</p>
<p>注销用户：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">user</span><span class="token operator">-></span><span class="token function">logout</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>请注意，启用 session 时注销用户才有意义。该方法将从内存和 session 中同时清理用户认证状态。默认情况下，它还会注销所有的用户会话数据。如果你希望保留这些会话数据，可以换成 <code v-pre>Yii::$app-&gt;user-&gt;logout(false)</code>。</p>
<h2 id="认证事件" tabindex="-1"><a class="header-anchor" href="#认证事件"><span>认证事件</span></a></h2>
<p><code v-pre>yii\web\User</code> 类在登录和注销流程引发一些事件。</p>
<ul>
<li><code v-pre>yii\web\User::EVENT_BEFORE_LOGIN</code>：在登录 <code v-pre>yii\web\User::login()</code> 时引发。如果事件句柄将事件对象的 <code v-pre>yii\web\UserEvent::isValid</code> 属性设为 false，登录流程将会被取消。</li>
<li><code v-pre>yii\web\User::EVENT_AFTER_LOGIN</code>：登录成功后引发。</li>
<li><code v-pre>yii\web\User::EVENT_BEFORE_LOGOUT</code>：注销 <code v-pre>yii\web\User::logout()</code> 前引发。如果事件句柄将事件对象的 <code v-pre>yii\web\UserEvent::isValid</code> 属性设为 false，注销流程将会被取消。</li>
<li><code v-pre>yii\web\User::EVENT_AFTER_LOGOUT</code>：成功注销后引发。</li>
</ul>
<p>你可以通过响应这些事件来实现一些类似登录统计、在线人数统计的功能。例如, 在登录后 <code v-pre>yii\web\User::EVENT_AFTER_LOGIN</code> 的处理程序，你可以将用户的登录时间和IP记录到 <code v-pre>user</code> 表中。</p>
<blockquote>
<p>💖喜欢本文档的，欢迎点赞、收藏、留言或转发，谢谢支持！<br>
作者邮箱：zhuzixian520@126.com，github地址：<a href="https://github.com/zhuzixian520" target="_blank" rel="noopener noreferrer">github.com/zhuzixian520</a></p>
</blockquote>
</div></template>


